<template>
  <div class="relative !h-full w-full overflow-hidden" ref="el"></div>
</template>

<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watchEffect, watch, unref, nextTick } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import { useAppStore } from '@/store/modules/app'
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn'
import CodeMirror from 'codemirror'
import { MODE } from './../typing'
// css
import './codemirror.css'
import 'codemirror/theme/idea.css'
import 'codemirror/theme/material-palenight.css'
// modes
import 'codemirror/mode/javascript/javascript'
import 'codemirror/mode/css/css'
import 'codemirror/mode/htmlmixed/htmlmixed'

const props = defineProps({
  mode: {
    type: String as PropType<MODE>,
    default: MODE.JSON,
    validator(value: any) {
      // 这个值必须匹配下列字符串中的一个
      return Object.values(MODE).includes(value)
    }
  },
  value: { type: String, default: '' },
  readonly: { type: Boolean, default: false }
})

const emit = defineEmits(['change'])

const el = ref()
let editor: Nullable<CodeMirror.Editor>

const debounceRefresh = useDebounceFn(refresh, 100)
const appStore = useAppStore()

watch(
  () => props.value,
  async (value) => {
    await nextTick()
    const oldValue = editor?.getValue()
    if (value !== oldValue) {
      editor?.setValue(value ? value : '')
    }
  },
  { flush: 'post' }
)

watchEffect(() => {
  editor?.setOption('mode', props.mode)
})

watch(
  () => appStore.getDarkMode,
  async () => {
    setTheme()
  },
  {
    immediate: true
  }
)

function setTheme() {
  unref(editor)?.setOption('theme', appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight')
}

function refresh() {
  editor?.refresh()
}

async function init() {
  const addonOptions = {
    autoCloseBrackets: true,
    autoCloseTags: true,
    foldGutter: true,
    gutters: ['CodeMirror-linenumbers']
  }

  editor = CodeMirror(el.value!, {
    value: '',
    mode: props.mode,
    readOnly: props.readonly,
    tabSize: 2,
    theme: 'material-palenight',
    lineWrapping: true,
    lineNumbers: true,
    ...addonOptions
  })
  editor?.setValue(props.value)
  setTheme()
  editor?.on('change', () => {
    emit('change', editor?.getValue())
  })
}

onMounted(async () => {
  await nextTick()
  init()
  useWindowSizeFn(debounceRefresh)
})

onUnmounted(() => {
  editor = null
})
</script>
